﻿/* xxt encryptor by aliver               */
/* Version 2.0                           */
/*                                       */
/* An implementation of xxtea encryption */
/* A very small (code wise) secure block */
/* cipher. Supposedly faster than btea   */
/* and for files of any non-trival size  */
/* it's gonna be way faster than TEA or  */
/* IDEA. At worst it's something like 4x */
/* the speed of DES, and more secure by  */
/* a factor of 2^72 ~ 4.7 x 10^21        */
/* My tests seemed to indicate that xxt  */
/* is about 3x the speed of mcrypt with  */
/* the same options. Not that mcrypt is  */
/* not a great piece of work. It is.     */
/*                                       */
/* The xxtea algorithm was designed by:  */
/* David Wheeler and Roger Needham. Some */
/* code from their publications was used */
/* for some of the encryption portions.  */
/* However it was altered to be 64 bit   */
/* architecture friendly.                */
/*                                       */
/* Rain Forest Puppy, Zen Parse, Steven  */
/* M. Christey, Hank Leininger, Daniel   */
/* Hartmeier, and anyone I forgot who    */
/* provided me with feedback, bugfixes,  */
/* or pointed out errors in the original */
/* release of xxt                        */
/*****************************************/
/* CHANGE LOG - CHANGE LOG - CHANGE LOG  */
/* Version 2                             */
/* o Tried to mitigate race condition in */
/*   -g by wiping the environment var as */
/*   soon as it's copied.                */
/* o Checked for NULL pointer on -g      */
/* o Added use of O_EXCL for opening the */
/*   output file.                        */
/* o Fixed problem whereby the hash of   */
/*   the key was being reduced to 64bits */
/*   since I was using a char array that */
/*   was being populated with ascii hex  */
/*   values rather than just binary      */
/* o Moved the argv trashing loop up so  */
/*   that it happens before any file I/O */
/* o Fixed an issue whereby someone may  */
/*   create a malicious xxt crypted file */
/*   with a bad filesize header. Now I   */
/*   check for this.                     */
/* o Checked for access to read & write  */
/*   to file in case someone is dumb     */
/*   enough to make this setuid root     */
/* o Added the -f functionality to use a */
/*   whole file as the key. The file is  */
/*   hashed to a 128 bit key.            */
/*****************************************/
/* Compile with cc -O3 -o xxt xxt.c      */
/* "xxt -h" for help, after that         */
/* tested on IRIX, Solaris, Linux        */
/* NetBSD, and AIX                       */

/*****************************************/
/* CHANGE LOG - CHANGE LOG - CHANGE LOG  */
/* Anagha Mudigonda version 3*/
/* I used most alivers logic as is but I */
/* removed main and a lot of functions I */
/* do not need for kamaelia. Also I use  */
/* my own keygen function so I removed   */
/* the MD5 related code                  */
/*****************************************/
using System;

public class XXTea
{
    const uint XXTEA_DELTA = (uint)0x9e3779b9;

    static uint XXTEA_MX(uint z, uint y, uint p, uint e, uint sum, uint[] k)
    {
        return (z >> 5 ^ y << 2) + (y >> 3 ^ z << 4) ^ (sum ^ y) + (k[p & 3 ^ e] ^ z);
    }

    static void xxtea_long_encrypt(uint[] v, uint len, uint[] k)
    {
        uint n = len - 1;
        uint z = v[n], y = v[0], p, q = 6 + 52 / (n + 1), sum = 0, e;
        if (n < 1)
        {
            return;
        }

        while (0 < q--)
        {
            sum += XXTEA_DELTA;
            e = sum >> 2 & 3;
            for (p = 0; p < n; p++)
            {
                y = v[p + 1];
                z = v[p] += XXTEA_MX(z, y, p, e, sum, k);
            }
            y = v[0];
            z = v[n] += XXTEA_MX(z, y, p, e, sum, k);
        }
    }

    static void xxtea_long_decrypt(uint[] v, uint len, uint[] k)
    {
        uint n = len - 1;
        uint z = v[n], y = v[0], p, q = 6 + 52 / (n + 1), sum = q * XXTEA_DELTA, e;
        if (n < 1)
        {
            return;
        }

        while (sum != 0)
        {
            e = sum >> 2 & 3;
            for (p = n; p > 0; p--)
            {
                z = v[p - 1];
                y = v[p] -= XXTEA_MX(z, y, p, e, sum, k);
            }
            z = v[n];
            y = v[0] -= XXTEA_MX(z, y, p, e, sum, k);
            sum -= XXTEA_DELTA;
        }
    }

    static uint[] xxtea_to_long_array(byte[] data, uint len, int include_length, out uint ret_len)
    {
        uint i, n;
        uint[] result;
        n = len >> 2;
        n = (((len & 3) == 0) ? n : n + 1);
        if (include_length > 0)
        {
            result = new uint[((n + 1) << 2)];
            result[n] = len;
            ret_len = n + 1;
        }
        else
        {
            result = new uint[n << 2];
            ret_len = n;
        }

        for (i = 0; i < len; i++)
        {
            result[i >> 2] |= (uint)data[i] << (byte)(((i & 3) << 3));
        }
        return result;
    }

    static byte[] xxtea_to_byte_array(uint[] data, uint len, int include_length, out uint ret_len)
    {
        uint i, n, m;
        byte[] result;
        n = len << 2;
        if (include_length > 0)
        {
            m = data[len - 1];
            if ((m < n - 7) || (m > n - 4))
            {
                ret_len = 0;
                return null;
            }
            n = m;
        }
        result = new byte[n];
        for (i = 0; i < n; i++)
        {
            result[i] = (byte)((data[i >> 2] >> (byte)(((i & 3) << 3)) & 0xff));
        }
        ret_len = n;
        return result;
    }

    static public byte[] xxtea_encrypt(byte[] data, uint len, byte[] key, out uint ret_len)
    {
        byte[] result;
        uint[] v;
        uint[] k;
        uint v_len = 0;
        uint k_len = 0;

        v = xxtea_to_long_array(data, len, 1, out v_len);
        k = xxtea_to_long_array(key, 16, 0, out k_len);
        xxtea_long_encrypt(v, v_len, k);
        result = xxtea_to_byte_array(v, v_len, 0, out ret_len);
        return result;
    }

    static public byte[] xxtea_decrypt(byte[] data, uint len, byte[] key, out uint ret_len)
    {
        byte[] result;
        uint[] v;
        uint[] k;
        uint v_len;
        uint k_len;

        v = xxtea_to_long_array(data, len, 0, out v_len);
        k = xxtea_to_long_array(key, 16, 0, out k_len);
        xxtea_long_decrypt(v, v_len, k);
        result = xxtea_to_byte_array(v, v_len, 1, out ret_len);
        return result;
    }
}

